﻿using System;
using System.Reflection;
using System.Reflection.Emit;

namespace NForth3
{
    class Stack
    {
        public const int STACK_SIZE = 1024 * 1024;
        public Type ClassType { set; get; }
        public FieldInfo IndexField { set; get; }
        public FieldInfo StackField { set; get; }

        public Stack(NForth nforth, string name)
        {
            CreateType(nforth, name);

            // initialize stack
            ClassType.GetMethod("Init").Invoke(null, null);
        }

        private void CreateType(NForth nforth, string name)
        {
            ModuleBuilder module = nforth.Assembly.DefineDynamicModule(name);
            TypeBuilder type = module.DefineType(name, TypeAttributes.Public);

            // fields
            FieldBuilder index_field = type.DefineField("Index", typeof(uint), FieldAttributes.Static | FieldAttributes.Public);
            FieldBuilder stack_field = type.DefineField("Stack", typeof(int[]), FieldAttributes.Static | FieldAttributes.Public);

            // init method
            MethodBuilder init = type.DefineMethod("Init", MethodAttributes.Public | MethodAttributes.Static);
            ILGenerator il = init.GetILGenerator();
            il.Emit(OpCodes.Ldc_I4, STACK_SIZE);
            il.Emit(OpCodes.Newarr, typeof(int));
            il.Emit(OpCodes.Stsfld, stack_field);
            il.Emit(OpCodes.Ldc_I4_0);
            il.Emit(OpCodes.Stsfld, index_field);
            il.Emit(OpCodes.Ret);

            type.CreateType();
            module.CreateGlobalFunctions();

            ClassType = module.GetType(name);
            IndexField = ClassType.GetField("Index");
            StackField = ClassType.GetField("Stack");
        }
    }
}
